package com.flyqiu.common.tools;

import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

/**
 * Map工具类，提供了一系列操作Map中集合的方法
 */
public class MapTools {
    /**
     * 根据key从map中获取值。如果key不存在，则在map中创建这个key，并为其创建一个List集合。
     *
     * @param map map对象
     * @param key 键
     * @param <T> 对象类型
     * @return 关联于key的List集合，如果key不存在则返回一个新创建的空List
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> getListByKey(Map map, Serializable key) {
        if (key == null) throw new RuntimeException("key is not null");
        if (map == null) throw new RuntimeException("map is not null");
        List<T> result;
        if (!map.containsKey(key)) {
            result = new ArrayList<>();
            map.put(key, result);
        }
        result = (List<T>) map.get(key);
        return result;
    }

    /**
     * 向map中指定的key对应的List中添加一个值。如果key不存在，则先创建该key及对应的空List。
     *
     * @param map   map对象
     * @param key   键
     * @param value 值
     * @param <T>   对象类型
     */
    public static <T> void mapListAddValue(Map map, Serializable key, T value) {
        if (key == null) throw new RuntimeException("key is not null");
        if (map == null) throw new RuntimeException("map is not null");
        if (value == null) throw new RuntimeException("value is not null");
        List<T> list = getListByKey(map, key);
        list.add(value);
    }

    /**
     * 向map中指定的key对应的List中添加一个List的值。如果key不存在，则先创建该key及对应的空List。
     *
     * @param map   map对象
     * @param key   键
     * @param value 要添加的List值
     * @param <T>   对象类型
     */
    public static <T> void mapAddList(Map map, Serializable key, List<T> value) {
        if (key == null) throw new RuntimeException("key is not null");
        if (map == null) throw new RuntimeException("map is not null");
        if (value == null) throw new RuntimeException("value is not null");
        List<T> list = getListByKey(map, key);
        list.addAll(value);
    }

    /**
     * 根据提供的keys字符串和分隔符，将keys拆分后，分别为每个key在map中添加一个值。
     * 如果key不存在，则先创建该key及对应的空List。
     *
     * @param map      map对象
     * @param value    值
     * @param keys     键的字符串表示，多个键之间使用keySplit分隔
     * @param keySplit 键字符串的分隔符
     * @param <T>      对象类型
     */
    public static <T> void mapListAddValue(Map map, T value, String keys, String keySplit) {
        if (StringUtils.isEmpty(keys) || StringUtils.isEmpty(keySplit) || !keys.contains(keySplit)) {
            MapTools.mapListAddValue(map, keys, value);
            return;
        }
        String[] split = keys.split(keySplit);
        for (String key : split) {
            if (StringUtils.isEmpty(key)) {
                System.out.println("value:" + value.getClass().getName() + " key contain Empty key");
                continue;
            }
            mapListAddValue(map, key, value);
        }
    }

    /**
     * 从map中指定key的List中移除指定的值。
     *
     * @param map   map对象
     * @param key   键
     * @param value 要移除的值
     * @param <T>   对象类型
     */
    public static <T> void mapListItemRemove(Map map, String key, T value) {
        if (map == null || map.isEmpty()) return;
        if (value == null) return;
        if (StringUtils.isEmpty(key)) {
            return;
        }
        List<T> listByKey = getListByKey(map, key);
        FlyQiuCollectionTools.remove(listByKey, value::equals);
    }

    /**
     * 从map中移除指定索引位置的元素。
     *
     * @param map   map对象
     * @param key   键
     * @param index 要移除的元素的索引
     * @param <T>   对象类型
     */
    public static <T> void mapRemove(Map map, String key, int index) {
        if (map == null || map.isEmpty()) return;
        if (index < 0) return;
        if (StringUtils.isEmpty(key)) {
            return;
        }
        List<Object> list = getListByKey(map, key);
        if (list == null || list.isEmpty()) return;
        if (index >= list.size()) {
            return;
        }
        list.remove(index);
    }

    /**
     * 根据提供的keys字符串和分隔符，将keys拆分后，从map中移除这些key。
     *
     * @param map      map对象
     * @param keys     键的字符串表示，多个键之间使用keySplit分隔
     * @param keySplit 键字符串的分隔符
     */
    public static <T> void mapRemove(Map map, String keys, String keySplit) {
        if (map == null || map.isEmpty()) return;
        if (StringUtils.isEmpty(keys)) {
            return;
        }
        if (StringUtils.isEmpty(keySplit) || !keys.contains(keySplit)) {
            map.remove(keys);
            return;
        }
        String[] key = keys.split(keySplit);
        for (String k : key) {
            map.remove(k);
        }
    }

    /**
     * 对map中指定key的List中的每个元素应用提供的Consumer函数。
     *
     * @param map      map对象
     * @param key      键
     * @param consumer 消费函数
     * @param <T>      对象类型
     */
    public static <T> void mapListConsumerItem(Map map, Serializable key, Consumer<T> consumer) {
        if (!map.containsKey(key)) return;
        List<T> list = getListByKey(map, key);
        FlyQiuCollectionTools.iterator(list, consumer);
    }

    /**
     * 遍历map中的每一对键值对，并对每个键值对应用提供的MapConsumer函数。
     *
     * @param map      map对象
     * @param consumer 消费函数
     * @param <K>      键的类型
     * @param <V>      值的类型
     */
    public static <K, V> void iterator(Map<K, V> map, MapConsumer<K, V> consumer) {
        if (FlyQiuCollectionTools.isEmpty(map)) {
            return;
        }
        Set<Map.Entry<K, V>> entries = map.entrySet();
        for (Map.Entry<K, V> next : entries) {
            consumer.accept(next.getKey(), next.getValue());
        }
    }

    /**
     * 判断 Map 中是否存在指定的属性路径。
     *
     * @param map          要检查的 Map。
     * @param propertyPath 属性路径，使用指定的分隔符分隔层级。
     * @param delimiter    分隔符，默认为“.”。
     * @return 如果路径中的每个层级都存在，则返回 true；否则返回 false。
     */
    public static boolean hasPath(Map<String, ?> map, String propertyPath, String delimiter) {
        if (map == null || propertyPath == null || propertyPath.isEmpty()) {
            return false;
        }
        if (StringTools.isBlank(delimiter)) {
            throw new IllegalArgumentException("Delimiter cannot be blank");
        }

        String[] pathParts = propertyPath.split(delimiter);
        return hasPath(map, pathParts, 0);
    }

    /**
     * 使用默认分隔符检查 Map 中是否存在指定的属性路径。
     *
     * @param map          要检查的 Map。
     * @param propertyPath 属性路径，使用默认分隔符“.”分隔层级。
     * @return 如果路径中的每个层级都存在，则返回 true；否则返回 false。
     */
    public static boolean hasPath(Map<String, ?> map, String propertyPath) {
        return hasPath(map, propertyPath, ".");
    }

    /**
     * 递归检查 Map 中的属性路径。
     *
     * @param currentMap 当前层级的 Map。
     * @param pathParts  属性路径的各个部分。
     * @param index      当前检查的路径部分索引。
     * @return 如果路径中的每个层级都存在，则返回 true；否则返回 false。
     */
    private static boolean hasPath(Map<String, ?> currentMap, String[] pathParts, int index) {
        if (index >= pathParts.length) {
            return true; // 已经到达路径末尾
        }

        String part = pathParts[index];
        if (!currentMap.containsKey(part)) {
            return false;
        }

        Object value = currentMap.get(part);
        if (value instanceof Map) {
            // 如果当前层级的值是一个 Map，则递归检查下一层级
            //noinspection unchecked
            return hasPath((Map<String, ?>) value, pathParts, index + 1);
        } else if (value == null) {
            // 如果不是 Map 类型且值为 null，则表示路径不正确
            return false;
        } else {
            // 如果不是 Map 类型，则检查是否到达了路径的末尾
            return index == pathParts.length - 1;
        }
    }


}
